#ifndef STX_PLATFORM_POSIX_THREAD_ATTRIBUTES_HPP
#define STX_PLATFORM_POSIX_THREAD_ATTRIBUTES_HPP

#include <stx/platform/posix/thread/common.hpp>

namespace stx {

namespace posix {

class thread_attributes: noncopyable
{
public:
    
    typedef thread_attributes this_type;
    
    thread_attributes()
    {
        int ret = pthread_attr_init(&handle_);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_init");
        }
    }
    
    ~thread_attributes()
    {
        int ret = pthread_attr_destroy(&handle_);
        if (ret) {
            STX_DESTRUCTOR_SYSTEM_ERROR_VAL(ret, "pthread_attr_destroy");
        }
    }
    
    //  Values:
    //      PTHREAD_CREATE_DETACHED
    //      PTHREAD_CREATE_JOINABLE
    int detached_state() const
    {
        int detached;
        int ret = pthread_attr_getdetachstate(&handle_, &detached);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getdetachstate");
        }
        return detached;
    }
    
    this_type& set_detached_state(int detached)
    {
        int ret = pthread_attr_setdetachstate(&handle_, detached);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setdetachstate");
        }
        return *this;
    }
    
    #if STX_HAS_PTHREAD_ATTR_GETGUARDSIZE
    size_t guard_size() const
    {
        size_t size;
        int ret = pthread_attr_getguardsize(&handle_, &size);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getguardsize");
        }
        return size;
    }
    #endif
    
    #if STX_HAS_PTHREAD_ATTR_SETGUARDSIZE
    this_type& set_guard_size(size_t size)
    {
        int ret = pthread_attr_setguardsize(&handle_, size);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setguardsize");
        }
        return *this;
    }
    #endif
    
    int inherit_scheduling() //const // const qualifier fails with pthreads-win32 library
    {
        int inherit;
        int ret = pthread_attr_getinheritsched(&handle_, &inherit);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getguardsize");
        }
        return inherit;
    }
    
    this_type& set_inherit_scheduling(int inherit)
    {
        int ret = pthread_attr_setinheritsched(&handle_, inherit);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setinheritsched");
        }
        return *this;
    }
    
    struct sched_param& scheduling_parameters(struct sched_param& param) const
    {
        int ret = pthread_attr_getschedparam(&handle_, &param);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getschedparam");
        }
        return param;
    }
    
    this_type& set_scheduling_parameters(const struct sched_param& param)
    {
        int ret = pthread_attr_setschedparam(&handle_, &param);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setschedparam");
        }
        return *this;
    }
    
    int scheduling_policy() //const // const qualifier fails with pthreads-win32 library
    {
        int policy;
        int ret = pthread_attr_getschedpolicy(&handle_, &policy);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getschedpolicy");
        }
        return policy;
    }
    
    this_type& set_scheduling_policy(int policy)
    {
        int ret = pthread_attr_setschedpolicy(&handle_, policy);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setschedpolicy");
        }
        return *this;
    }
    
    int contention_scope() const
    {
        int scope;
        int ret = pthread_attr_getscope(&handle_, &scope);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getscope");
        }
        return scope;
    }
    
    this_type& set_contention_scope(int scope)
    {
        int ret = pthread_attr_setscope(&handle_, scope);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setscope");
        }
        return *this;
    }
    
    #if STX_HAS_PTHREAD_ATTR_GETSTACK
    void get_stack(void*& stack_addr, size_t& stack_size) const
    {
        int ret = pthread_attr_getstack(&handle_, &stack_addr, &stack_size);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getstackaddr");
        }
    }
    #endif
    
    #if STX_HAS_PTHREAD_ATTR_SETSTACK
    this_type& set_stack(void* stack_addr, size_t stack_size)
    {
        int ret = pthread_attr_setstack(&handle_, stack_addr, stack_size);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setstack");
        }
        return *this;
    }
    #endif
    
    void* stack_address() const
    {
        void* p;
        int ret = pthread_attr_getstackaddr(&handle_, &p);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getstackaddr");
        }
        return p;
    }
    
    this_type& set_stack_address(void* p)
    {
        int ret = pthread_attr_setstackaddr(&handle_, p);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setstackaddr");
        }
        return *this;
    }
    
    size_t stack_size() const
    {
        size_t size;
        int ret = pthread_attr_getstacksize(&handle_, &size);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_getstacksize");
        }
        return size;
    }
    
    this_type& set_stack_size(size_t size)
    {
        int ret = pthread_attr_setstacksize(&handle_, size);
        if (ret) {
            STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_attr_setstacksize");
        }
        return *this;
    }
    
    typedef pthread_attr_t handle_type;
    
    handle_type* handle()
    {
        return &handle_;
    }
    
    const handle_type* handle() const
    {
        return &handle_;
    }
    
private:
    
    handle_type handle_;
};

} // namespace posix

} // namespace stx

#endif // STX_PLATFORM_POSIX_THREAD_ATTRIBUTES_HPP
